//
//  parse_rdf.cpp
//  cobalt
//
//  Created by Nicolas Godbout on 2013-08-19.
//  Copyright (c) 2013 Nicolas Godbout. All rights reserved.
//

#include "parse_rdf.h"
// #include "optional.hpp"

#include <regex>

extern std::regex regex_nt_comment;

TripleList
nt_ntripleDoc (PState* st) {
    return TripleList();
}

Triple*
nt_line (PState* st) {
    nt_eat_hws(st->input);
    char *input_start = st->input->base + st->input->offset;
    if ( std::regex_match(input_start, regex_nt_comment) )  // ## BUG: advance input !!
        return nullptr;
    else if ( Triple* t = nt_triple(st) )
        return t;

    return nullptr;
}

Triple*
nt_triple (PState* st) {
    RDFNode s(ttl_subject(st));
    if ( !s || !nt_eat_hws(st->input) )
        return nullptr;

    RDFNode p(ttl_predicate(st));
    if ( !p || !nt_eat_hws(st->input) )
        return nullptr;
    
    RDFNode o(ttl_object(st));
    if ( !o )
        return nullptr;
    
    return st->env->createTriple(s, p, o);
}

RDFNode
ttl_subject (PState* st) {
    RDFNode ret;
    if ( (ret = ttl_resource(st)) || (ret = ttl_blank(st)) )
        return ret;
    else
        return RDFNode();
}

RDFNode
ttl_predicate (PState* st) {
    return ttl_resource(st);
}

RDFNode
ttl_object (PState* st) {
    RDFNode ret;
    if ( (ret = ttl_resource(st)) || (ret = ttl_blank(st)) || (ret = ttl_literal(st)) )
        return ret;
    else
        return RDFNode();
}

RDFNode
ttl_resource (PState* st) {
    ByteString res(ttl_URIRef(st->input));
    if ( res ) {
        char* name = res.toCstr();
        return st->env->createNamedNode(name);
    }
    return RDFNode();
}

RDFNode
ttl_blank (PState* st) {
    ByteString res(ttl_Blank(st->input));
    if ( res ) {
        // ## TODO lookup blank ID is the parse state
        return st->env->createBlankNode();
    }
    else
        return RDFNode();
}

RDFNode
ttl_literal (PState* st) {
    ByteString res(ttl_Boolean(st->input));
    if ( res ) {
        if ( res.base[res.offset] == 't' || res.base[res.offset] == 'T' )
            return RDFNode::rdf_true;
        else
            return RDFNode::rdf_false;
    }
    res = ttl_Integer(st->input);
    if ( res ) {
        char* value = res.toCstr();
        return st->env->createLiteral(value, NULL, RDFNode::xsd_integer);
    }
    res = ttl_String(st->input);
    if ( res ) {
        char* value = res.toCstr();
        // ## parse language or datatype
        return st->env->createLiteral(value, NULL, RDFNode::xsd_string);
    } else
        return RDFNode();
}

// Regular expressions for recognizing tokens

static std::regex
regex_ttl_ws("^([[:space:]]|#[^\r\n]*)+",
             std::regex::optimize | std::regex::extended | std::regex::nosubs);

std::regex
regex_nt_comment("^#[^\r\n]*(\r\n?|\n)",
                 std::regex::optimize | std::regex::extended | std::regex::nosubs);

static std::regex
regex_ttl_URIRef("^<([ -=?-\[^-~]*)>",
                 std::regex::optimize | std::regex::extended);
static std::regex
regex_ttl_Blank("^_:([[:alpha:]_][[:alnum:]_-]*)",
                std::regex::optimize | std::regex::extended);
static std::regex
regex_ttl_String("^\"([] !#-\[^-~]*)\"",
                 std::regex::optimize | std::regex::extended);
static std::regex
regex_ttl_Integer("^(\\+|-)?[[:digit:]]+",
                  std::regex::optimize | std::regex::extended);
static std::regex
regex_ttl_Boolean("^(true|false)",
                  std::regex::optimize | std::regex::extended | std::regex::icase);


ByteString
parse_token (const std::regex& pattern, ByteString* input) {
    char* input_start = input->base + input->offset;
    std::cmatch match{};
    if ( std::regex_search(input_start, match, pattern) ) {
        ByteString ret(input->base,
                       match[1].first - input->base,
                       match[1].second - match[1].first);
        
        input->offset += match[0].second - match[0].first;
        input->length -= match[0].second - match[0].first;
        
        return ret;
    } else
        return ByteString();
}

inline bool
nt_eat_hws (ByteString* input) {
    size_t offset = input->offset;
    while ( input->base[offset] == ' ' || input->base[offset] == '\t' )
        offset++;
    if ( offset == input->offset )
        return false;
    else {
        input->length -= offset - input->offset;
        input->offset = offset;
        return true;
    }
}

inline ByteString
ttl_ws (ByteString* input) {
    return parse_token(regex_ttl_ws, input);
}

inline ByteString
ttl_URIRef (ByteString* input) {
    return parse_token(regex_ttl_URIRef, input);
}

inline ByteString
ttl_Blank (ByteString* input) {
    return parse_token(regex_ttl_Blank, input);
}

inline ByteString
ttl_String (ByteString* input) {
    return parse_token(regex_ttl_String, input);
}

inline ByteString
ttl_Boolean (ByteString* input) {
    return parse_token(regex_ttl_Boolean, input);
}

inline ByteString
ttl_Integer (ByteString* input) {
    return parse_token(regex_ttl_Integer, input);
}

int test_parse_ttl() {
    ByteString bs = { 0, 0, 0} ;
    ByteString res;
    
    bs.base = (char *)"-123 asdf";
    res = ttl_Integer(&bs);
    assert(res);
    
    bs.base = (char *)"\"Hello World!\"";
    bs.offset = 0;
    res = ttl_String(&bs);
    assert(res);
    
    bs.base = (char *)"<http://www.cobalt27.org/2013/cobalt#type> _:a";
    bs.offset = 0;
    res = ttl_URIRef(&bs);
    assert(res);
    
    bs.base = (char *)"_:b0 <http://apple.com>";
    bs.offset = 0;
    res = ttl_Blank(&bs);
    assert(res);
    
    RDFEnvironment env;
    PState st;
    st.input = &bs;
    st.env = &env;
    Triple *triple;
    
    bs.base = (char *)"_:a <rdf://value> -22";
    bs.offset = 0;
    triple = nt_triple(&st);
    assert(triple);
    delete triple;
    
    return 0;
}
